from apps.goods.models import SKU
from rest_framework import serializers

# SKUSpecification 序列化器  -- sku规格和规格选项
#为何定义spu_id 和 category_id ?
#因为spu_id 和 category_id 是sku表中的外键，但是sku表中没有这两个字段，所以需要手动添加这两个字段
# SKUSpecification 序列化器 -- sku规格和规格选项
from apps.goods.models import SKU,SKUSpecification
from rest_framework import serializers
class SKUSpecificationModelSerializer(serializers.ModelSerializer):
    # 规格id 和 规格选项id
    # 规格id 是 SKUSpecification 表中的外键
    # 规格选项id 是 SKUSpecificationOption 表中的外键
    # spec_id 和 option_id 不是模型类中的字段，所以需要手动添加
    # spec_id 和 option_id 用于接收前端传递的数据, 然后在 create 方法中 手动添加到 validated_data 中
    spec_id=serializers.IntegerField()
    option_id=serializers.IntegerField()

    class Meta:
        model=SKUSpecification
        fields=['spec_id','option_id']
# SKU 序列化器
class SKUModelSerializer(serializers.ModelSerializer):

    """
    前端传递的数据形式是
    {
        caption: "12不想13香"
        category_id: 115            ~~~~~~
        cost_price: "1"
        is_launched: "true"
        market_price: "11"
        name: "13香"
        price: "1"
        specs: [{spec_id: "4", option_id: 8}, {spec_id: "5", option_id: 11}]
        spu_id: 2                   ~~~~~~~
        stock: "1"
    }

    Q1. 外键中 spu和category的数据  前端是以 category_id 和 spu_id 的形式传递的 所以我们的序列化器 要改变
        spu_id=serializers.IntegerField()
        category_id=serializers.IntegerField()

    Q2. 我们 通过 添加 spu_id 和 category_id 能接收前端的数据,
        但是并没有改变 系统自动生成的  spu和category 这2个字段必传的选项
        如何去解决呢???
        ① 改为 required=False
        ② 为了配合查询数据的展示 我们 重写 spu 和category 这2个字段

        spu=serializers.StringRelatedField()
        category=serializers.StringRelatedField()

        新的疑问!!!  默认的字段是 required=True

        StringRelatedField 本质是什么???
        本质是 获取 关联模型的 __str__ 里的数据

    Q3. 当我们的sku保存了之后, sku的最终 (规格)选项 也应该入库
        specs: [{spec_id: "4", option_id: 8}, {spec_id: "5", option_id: 11}] 这个没有入库!!!!

        # validated_data  其实是等于 我们序列化器 通过字段 验证的数据
        # 当前 我们在 序列化器中 并没有定义 specs 所以 validated_data 里没有 specs
        # 我们想要获取 specs 有至少2种方法
        # 1. 添加 specs 字段
        # 2. 通过 断点我们发现  self.context['request'] 可以获取 当前的请求对象.


    """
    spu_id=serializers.IntegerField()
    category_id=serializers.IntegerField()

    spu=serializers.StringRelatedField(required=False)
    category=serializers.StringRelatedField()

    # 规格和规格选项  是一个 列表 所以 我们要添加 many=True
    specs=SKUSpecificationModelSerializer(many=True)

    class Meta:
        model=SKU
        fields='__all__'


    def create(self, validated_data):
        # validated_data  其实是等于 我们序列化器 通过字段 验证的数据
        # 当前 我们在 序列化器中 并没有定义 specs 所以 validated_data 里没有 specs
        # 我们想要获取 specs 有至少2种方法
        # 1. 添加 specs 字段
        # 2. 通过 断点我们发现  self.context['request'] 可以获取 当前的请求对象.

        # 保存 sku和 sku规格.规格选项

        # 1. 把 规格和规格选项 单独获取出来
        # pop 方法 会从 validated_data 中 把 specs 这个键值对 给 pop 出来
        # 并赋值给 specs 变量
        # pop意思是 弹出, 就是把这个键值对 从 validated_data 中删除掉
        specs=validated_data.pop('specs')

        from django.db import transaction
        # 2. 保存 sku数据
        # 事务的相关教程和知识点如下:
        # 首先,我们需要导入 transaction 模块
        # 然后,我们可以使用 with 语句来创建一个事务块
        # 3. 我们可以在事务块中 进行数据库操作
        # 4. 如果在事务块中 发生了异常, 我们可以使用 try...except 来捕获异常
        # 5. 如果发生了异常, 我们可以使用 transaction.savepoint_rollback 来回滚事务
        # 6. 如果没有发生异常, 我们可以使用 transaction.savepoint_commit 来提交事务
        with transaction.atomic():
            #①事务开始点
            save_point=transaction.savepoint()

            try:
                # 2. 先保存sku数据
                sku=SKU.objects.create(**validated_data)
                # 3. 对规格和规格选项进行遍历保存
                for spec in specs:
                    # spec = {spec_id: "4", option_id: 8}
                    SKUSpecification.objects.create(sku=sku,**spec)
            except Exception:
                # ② 事务回滚点
                transaction.savepoint_rollback(save_point)
            else:
                # ③ 事务提交点
                transaction.savepoint_commit(save_point)


        return sku


    def update(self, instance, validated_data):

        # 1. pop 规格和规格选项数据 (把内层字典数据 pop出来)
        specs=validated_data.pop('specs')
        # 2. 更新 sku数据
        # super() 调用父类的 update来实现数据的更新
        super().update(instance,validated_data)

        # ②
        # for attr, value in validated_data.items():
        #     setattr(instance, attr, value)
        # instance.save()

        #3. 更新规格和规格选项
        # 参数的解释,sku=instance的意思是 我们要更新的是 instance 这个 sku对象
        # spec_id=spec.get('spec_id') 规格id
        # option_id=spec.get('option_id') 规格选项id
        # SKUSpecification.objects.filter(sku=instance,spec_id=spec_id).update(option_id=option_id)的意思是
        # 我们要更新的是 instance 这个 sku对象的 规格id 为 spec_id 的规格选项 为 option_id
        for spec in specs:
            #  spec = {spec_id: "4", option_id: 8}
            SKUSpecification.objects.filter(sku=instance,spec_id=spec.get('spec_id')).update(option_id=spec.get('option_id'))

        return instance


##########三级分类数据序列化器############################################
from apps.goods.models import GoodsCategory

class GoodsCategoryModelSerializer(serializers.ModelSerializer):

    class Meta:
        model=GoodsCategory
        fields=['id','name']


##########spu数据序列化器############################################
from apps.goods.models import SPU
class SPUModelSerializer(serializers.ModelSerializer):
    
    class Meta:
        model=SPU
        fields='__all__'

#########################SPU 规格和规格选项 数据序列化器############################################



##########SPU 规格和规格选项的序列化器############################################
# SPU 规格 和 规格选项 的序列化器
from apps.goods.models import SPUSpecification,SpecificationOption

# 规格选项 序列化器
class OptionModelSerializer(serializers.ModelSerializer):

    class Meta:
        model=SpecificationOption
        fields=['id','value']

# SPU 规格 序列化器
class SpecsModelSerializer(serializers.ModelSerializer):

    options = OptionModelSerializer(many=True)
    class Meta:
        model=SPUSpecification
        fields=['id','name','options']
